package com.iyang.bootredislock.utils;

import com.iyang.bootredislock.config.JedisManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;

import java.util.List;

/**
 * 分布式锁工具类
 * @Author: Mu_YI
 * @Date: 2019/4/7 18:57
 * @Version 1.0
 */

public class RedisDistributedLock {

    private static final Logger LOGGER = LoggerFactory.getLogger(RedisDistributedLock.class);
    private static final String LOCK_SUCCESS = "OK";
    private static final Integer RELEASE_SUCCESS = 1;
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
    private static JedisPool jedisPool = JedisManager.getJedisPool();

    /**
     * 加锁
     * @param lockName  锁名字；对应被争用的共享资源
     * @param randomValue 随机值，需要保持全局唯一，便于释放时校验锁的持有者
     * @param expireTime 过期时间，到期后自动释放，防止出现问题时死锁，资源无法释放
     * @return
     */
    public static boolean acquireLock(String lockName,String randomValue,int expireTime){
        Jedis jedis = jedisPool.getResource();
        try{
            while (true){
                String result = jedis.set(lockName, randomValue, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
                if(LOCK_SUCCESS.equals(result)){
                    LOGGER.info("【Redis Lock】 success to acquire lock for [" +lockName + "]",expireTime);
                    return true;
                }
            }
        }catch (Exception e){
            LOGGER.error(e.getMessage(),e);
        } finally {
           if(jedis != null){
               jedis.close();
           }
        }
        LOGGER.info("【Redis Lock】 failed to acquire lock for [" + lockName + "]");
        return false;
    }

    public  static boolean releaseLock(String lockName,String randomValue){
        Jedis jedis = jedisPool.getResource();
        try {
            // watch监听
            jedis.watch(lockName);
            if(randomValue.equals(jedis.get(lockName))){
                // 开启事务
                Transaction multi = jedis.multi();
                //添加操作到事务
                multi.del(lockName);
                // 执行事务
                List<Object> exec = multi.exec();
                if(RELEASE_SUCCESS.equals(exec.size())){
                    LOGGER.info("【Redis lock】success to release lock for [ "+lockName+" ]");
                    return true;
                }
            }
        }catch (Exception e){
            LOGGER.info("【Redis lock】failed to release lock for [ "+lockName+" ]");
            LOGGER.error(e.getMessage(),e);
        } finally {
            if(jedis != null){
                jedis.unwatch();
                jedis.close();
            }
        }
        return false;
    }
}
